/* */

#include "mapgrab.h"

uint qHash( const QPoint& p ) {

    return ( p.x() * 17 ^ p.y() );
}

MapGrab::MapGrab(QObject *parent) : QObject(parent) {

    width = 400;
    height = 300;
    zoom = 15;
    latitude = 39.7313160;
    longitude = -84.0855610;

    // Set the default color if no map is available
    m_emptyTile = QPixmap(tdim, tdim);
    m_emptyTile.fill(Qt::lightGray);

    m_currentLevel  = QPixmap(800,600);
    // Network cache to store necessary tiles -- Current size is 50 MB
    cache = new QNetworkDiskCache;
    cache->setMaximumCacheSize(200 * 1024^2); // Hoping for 200 MB
    cache->setCacheDirectory("../map_cache");

    m_manager.setCache(cache);
    // Connect signal so we know how and when to handle the data
    connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
            this,       SLOT(handleNetworkData(QNetworkReply*)));
}

void MapGrab::invalidate() {
    if (width <= 0 || height <= 0)
        return;

    // Convert convert center point lat/long and zoom to
    QPointF ct = coord2tile(latitude, longitude, zoom);
    qreal tileX = ct.x();
    qreal tileY = ct.y();

    // top-left corner of the center tile
    int xp = width / 2 - (tileX - floor(tileX)) * tdim;
    int yp = height / 2 - (tileY - floor(tileY)) * tdim;

    // first tile vertical and horizontal
    int xa = (xp + tdim - 1) / tdim;
    int ya = (yp + tdim - 1) / tdim;
    int xs = static_cast<int>(tileX) - xa;
    int ys = static_cast<int>(tileY) - ya;

    // offset for top-left tile
    m_offset = QPoint(xp - xa * tdim, yp - ya * tdim);

    // last tile vertical and horizontal
    int xe = static_cast<int>(tileX) + (width - xp - 1) / tdim;
    int ye = static_cast<int>(tileY) + (height - yp - 1) / tdim;

    // build a rect
    m_tilesRect = QRect(xs, ys, xe - xs + 1, ye - ys + 1);

    if (m_url.isEmpty())
        download();

    emit updated(QRect(0, 0, width, height));
    cout << "Emitted update signal" << endl;
}

void MapGrab::handleNetworkData(QNetworkReply *reply) {
    QImage img;
    QPoint tp = reply->request().attribute(QNetworkRequest::User).toPoint();
    QUrl url = reply->url();
    if (!reply->error())
        if (!img.load(reply, 0))
            img = QImage();
    reply->deleteLater();
    m_tilePixmaps[tp] = QPixmap::fromImage(img);
    if (img.isNull())
        m_tilePixmaps[tp] = m_emptyTile;
    emit updated(tileRect(tp));

    // purge unused spaces
    QRect bound = m_tilesRect.adjusted(-2, -2, 2, 2);
    foreach(QPoint tp, m_tilePixmaps.keys())
    if (!bound.contains(tp))
        m_tilePixmaps.remove(tp);

    download();
}

void MapGrab::download() {
    QPoint grab(0, 0);
    for (int x = 0; x <= m_tilesRect.width(); ++x)
        for (int y = 0; y <= m_tilesRect.height(); ++y) {
            QPoint tp = m_tilesRect.topLeft() + QPoint(x, y);
            if (!m_tilePixmaps.contains(tp)) {
                grab = tp;
                break;
            }
        }
    if (grab == QPoint(0, 0)) {
        m_url = QUrl();
        return;
    }

    QString path = "http://tile.openstreetmap.org/%1/%2/%3.png";
    m_url = QUrl(path.arg(zoom).arg(grab.x()).arg(grab.y()));
    QNetworkRequest request;
    request.setUrl(m_url);
    request.setRawHeader("User-Agent", "QPlot -- Dustin W. Smith");
    request.setAttribute(QNetworkRequest::User, QVariant(grab));
    m_manager.get(request);
}

QRect MapGrab::tileRect(const QPoint &tp) {
    QPoint t = tp - m_tilesRect.topLeft();
    int x = t.x() * tdim + m_offset.x();
    int y = t.y() * tdim + m_offset.y();
    return QRect(x, y, tdim, tdim);
}

int MapGrab::zoomCalculation(qreal lat, qreal dist) {

    qint32 zoomLevel;

    qreal earthRadius;
    qreal rLat;
    // Convert lat to radians
    rLat = lat * M_PI / 180;
    earthRadius = 40075.16 * 1000;

    zoomLevel = log( earthRadius * cos(rLat) / dist ) / log( 2 ) - 8;

    return zoomLevel;

}

QPointF MapGrab::coord2tile(qreal lat, qreal lon, int zoom) {
    qreal zn = static_cast<qreal>(1 << zoom);
    qreal tx = (lon + 180.0) / 360.0;
    qreal ty = (1.0 - log(tan(lat * M_PI / 180.0) +
                              1.0 / cos(lat * M_PI / 180.0)) / M_PI) / 2.0;

        return QPointF(tx * zn, ty * zn);
}

QPointF MapGrab::tile2coord(qreal tileX, qreal tileY, int zoom) {

    qreal zn = static_cast<qreal>(1 << zoom);
    qreal lat = tileX / zn * 360.0 - 180.0;

    qreal n = M_PI - 2 * M_PI * tileY / zn;
    qreal lng = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));

    return QPointF( lat, lng );
}

void MapGrab::render(QPainter *p, const QRect &rect, const bool &resizeFlag) {

    m_currentLevel.scaledToHeight(rect.height(), Qt::FastTransformation);
    m_currentLevel.scaledToWidth(rect.width(), Qt::FastTransformation);

    if (!resizeFlag) {
        //locPaint->begin(&m_currentLevel);

        for (int x = 0; x <= m_tilesRect.width(); ++x)
            for (int y = 0; y <= m_tilesRect.height(); ++y) {
                QPoint tp(x + m_tilesRect.left(), y + m_tilesRect.top());
                QRect box = tileRect(tp);
                if (rect.intersects(box)) {
                    if (m_tilePixmaps.contains(tp)) {
                        p->drawPixmap(box, m_tilePixmaps.value(tp));
                   //     locPaint->drawPixmap(box, m_tilePixmaps.value(tp));
                    } else {
                        p->drawPixmap(box, m_emptyTile);
                   //     locPaint->drawPixmap(box, m_emptyTile);
                    }
                }
            }

       // locPaint->end();
    } else {
        qDebug() << "MapGreab::render:Resize Event";
        //p->drawPixmap( rect, m_currentLevel );
    }
}

/*MapGrab::MapGrab(QWidget *parent) : QWidget(parent) {

    pressed = false;
    snapped = false;
    zoomed  = false;
    invert  = false;

    m_normalMap = new SlippyMap(this);
    m_largeMap = new SlippyMap(this);
    connect(m_normalMap, SIGNAL(updated(QRect)), SLOT(updateMap(QRect)));
    connect(m_largeMap, SIGNAL(updated(QRect)), SLOT(update()));
}

void MapGrab::setCenter(qreal lat, qreal lng) {

    m_normalMap->latitude = lat;
    m_normalMap->longitude = lng;
    m_normalMap->invalidate();
    m_largeMap->invalidate();

}

void MapGrab::toggleNightMode() {

    invert = !invert;
    update();

}
void MapGrab::activateZoom() {
    zoomed = true;
    tapTimer.stop();
    m_largeMap->zoom = m_normalMap->zoom + 1;
    m_largeMap->width = m_normalMap->width * 2;
    m_largeMap->height = m_normalMap->height * 2;
    m_largeMap->latitude = m_normalMap->latitude;
    m_largeMap->longitude = m_normalMap->longitude;
    m_largeMap->invalidate();
    update();
}

void MapGrab::resizeEvent(QResizeEvent *) {
    m_normalMap->width = width();
    m_normalMap->height = height();
    m_normalMap->invalidate();
    m_largeMap->width = m_normalMap->width * 2;
    m_largeMap->height = m_normalMap->height * 2;
    m_largeMap->invalidate();
}

void MapGrab::paintEvent(QPaintEvent *event) {
    QPainter p;
    p.begin(this);
    m_normalMap->render(&p, event->rect());
    p.setPen(Qt::black);

    p.drawText(rect(),  Qt::AlignBottom | Qt::TextWordWrap,
               "Map data CCBYSA 2009 OpenStreetMap.org contributors");
    p.end();

    if (zoomed) {
        int dim = qMin(width(), height());
        int magnifierSize = qMin(MAX_MAGNIFIER, dim * 2 / 3);
        int radius = magnifierSize / 2;
        int ring = radius - 15;
        QSize box = QSize(magnifierSize, magnifierSize);

        // reupdate our mask
        if (maskPixmap.size() != box) {
            maskPixmap = QPixmap(box);
            maskPixmap.fill(Qt::transparent);

            QRadialGradient g;
            g.setCenter(radius, radius);
            g.setFocalPoint(radius, radius);
            g.setRadius(radius);
            g.setColorAt(1.0, QColor(255, 255, 255, 0));
            g.setColorAt(0.5, QColor(128, 128, 128, 255));

            QPainter mask(&maskPixmap);
            mask.setRenderHint(QPainter::Antialiasing);
            mask.setCompositionMode(QPainter::CompositionMode_Source);
            mask.setBrush(g);
            mask.setPen(Qt::NoPen);
            mask.drawRect(maskPixmap.rect());
            mask.setBrush(QColor(Qt::transparent));
            mask.drawEllipse(g.center(), ring, ring);
            mask.end();
        }

        QPoint center = dragPos - QPoint(0, radius);
        center = center + QPoint(0, radius / 2);
        QPoint corner = center - QPoint(radius, radius);

        QPoint xy = center * 2 - QPoint(radius, radius);

        // only set the dimension to the magnified portion
        if (zoomPixmap.size() != box) {
            zoomPixmap = QPixmap(box);
            zoomPixmap.fill(Qt::lightGray);
        }
        if (true) {
            QPainter p(&zoomPixmap);
            p.translate(-xy);
            m_largeMap->render(&p, QRect(xy, box));
            p.end();
        }

        QPainterPath clipPath;
        clipPath.addEllipse(center, ring, ring);

        QPainter p(this);
        p.setRenderHint(QPainter::Antialiasing);
        p.setClipPath(clipPath);
        p.drawPixmap(corner, zoomPixmap);
        p.setClipping(false);
        p.drawPixmap(corner, maskPixmap);
        p.setPen(Qt::gray);
        p.drawPath(clipPath);
    }
    if (invert) {
        QPainter p(this);
        p.setCompositionMode(QPainter::CompositionMode_Difference);
        p.fillRect(event->rect(), Qt::white);
        p.end();
    }
}

void MapGrab::timerEvent(QTimerEvent *) {
    if (!zoomed)
        activateZoom();
    update();
}*/
